package com.yunli.mall.service.impl;

import com.google.common.util.concurrent.RateLimiter;
import com.yunli.mall.common.exception.MallRuntimeException;
import com.yunli.mall.constant.RedisKeyPrefix;
import com.yunli.mall.enums.SeckillStateEnum;
import com.yunli.mall.repository.SeckillMapper;
import com.yunli.mall.schema.response.SeckillExecution;
import com.yunli.mall.service.SeckillService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * @author yaolonglong
 * @date 2020/9/21 22:37
 */
@Service
public class RedisSeckillServiceImpl implements SeckillService {
    private RateLimiter seckillRateLimiter = RateLimiter.create(500);
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    protected RedissonClient redissonClient;
    @Autowired
    private SeckillMapper seckillMapper;

    @Override
    public SeckillExecution executor(String seckillId, String phone) {
        if (seckillRateLimiter.tryAcquire()) {
            // 如果没有被限流器限制，则执行秒杀处理
            return handleSeckill(seckillId, phone);
        } else {    //如果被限流器限制，直接抛出访问限制的异常
            throw new MallRuntimeException(SeckillStateEnum.ACCESS_LIMIT.getDesc());
        }
    }

    public SeckillExecution handleSeckill(String seckillId, String phone) {
        RLock lock = redissonClient.getLock(RedisKeyPrefix.SECKILL_LOCK + seckillId);
        lock.lock();
        int inventery = (int) redisTemplate.opsForValue().get(RedisKeyPrefix.SECKILL_INVENTORY + seckillId);
        if (inventery < 1) {
            seckillMapper.clearInventory(seckillId);
            throw new MallRuntimeException(SeckillStateEnum.ACCESS_LIMIT.getDesc());
        }
        redisTemplate.opsForList().rightPush(RedisKeyPrefix.BOUGHT_USERS, seckillId + "-" + phone);
        redisTemplate.opsForValue().set(RedisKeyPrefix.SECKILL_INVENTORY + seckillId, inventery - 1);
        lock.unlock();
        return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS);
    }

    @Override
    public int result(String seckillId, String phone) {
        return 0;
    }
}
